#ifndef AVALON_FUNCTIONAL_HPP
#define AVALON_FUNCTIONAL_HPP
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <boost/type_traits.hpp>
#include "Avalon/Base/Base.hpp"

namespace boost {
namespace detail {
  template<typename T>
  struct function_traits_helper<boost::function<T>*> : boost::function<T> {};
}
}

namespace Avalon {
namespace Details {
  template<typename F, int arity>
  struct GetFunctionTypeHelper {};

  #define PARAMETER_TYPES(z, n, q)                                             \
    BOOST_PP_COMMA_IF(BOOST_PP_DEC(n))                                         \
    typename boost::function_traits<F>::arg##n##_type

  #define BOOST_PP_LOCAL_MACRO(n)                                              \
  template<typename F>                                                         \
  struct GetFunctionTypeHelper<F, n> {                                         \
    typedef boost::function<typename boost::function_traits<F>::result_type    \
      (BOOST_PP_REPEAT_FROM_TO(1, BOOST_PP_INC(n), PARAMETER_TYPES,))> Type;   \
  };

  #define BOOST_PP_LOCAL_LIMITS (0, BOOST_FUNCTION_MAX_ARGS)
  #include BOOST_PP_LOCAL_ITERATE()
  #undef PARAMETER_TYPES
}

  template<typename F>
  struct GetFunctionType {
    typedef typename Details::GetFunctionTypeHelper<F,
      boost::function_traits<F>::arity>::Type Type;
  };
}

#endif // AVALON_FUNCTIONAL_HPP
